package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"sync"
	"time"
)

var (
	Mu      sync.Mutex
	proxyip = flag.String("local.addr", "0.0.0.0:6500", "local.addr:proxy_server监听的本地ip与port.")
	endip   = flag.String("end.addr", "111.19.254.149:3000", "end.addr:end_server监听的本地ip与port.")
	apiadrr = flag.String("http.server", "127.0.0.1:8080", "http.server:api服务监听地址!")
)

/*
*************************主函数部分*********************************
 */
func main() {
	flag.Parse()
	Defaultapi.Proxynowlisten = *proxyip
	Defaultapi.Endnowlisten = *endip
	go server(Defaultapi.Proxynowlisten, Defaultapi.Endnowlisten)
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "proxyserver:now is ok!")
	apiserver(*apiadrr)
}

/*
***************************api部分**********************************
 */
var ListenToend = make(map[string]string, 5)             //显示proxy服务端监听地址对应的后端地址
var Proxylistentable = make(map[string]*net.Listener, 5) //ProxyListen注册表
type Apiserver struct {
	Proxynowlisten string             `json:"proxynowListenaddr"`
	Endnowlisten   string             `json:"endnowListenaddr"`
	Nowallmap      *map[string]string `json:"nowListenToendaddr"`
}
type Deltable struct {
	Listenaddr string   `json:"deleteaddr"`
	Handlisten []string `json:"nowlistenlist"`
}

var Defaultapi = &Apiserver{
	Nowallmap: &ListenToend,
}
var Defaultdel = &Deltable{}

//Api 主函数
func apiserver(httpaddr string) {
	http.HandleFunc("/api", f1)
	http.HandleFunc("/cancel", f2)
	http.HandleFunc("/", f3)
	err := http.ListenAndServe(httpaddr, nil)
	if err != nil {
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "api服务err: ", err)
		return
	}
}

//listentable注销function
func unlistentable(localaddr string) error {
	err := (*Proxylistentable[localaddr]).Close()
	if err != nil {
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), err)
		return err
	}
	Mu.Lock()
	delete(Proxylistentable, localaddr)
	delete(ListenToend, localaddr)
	Mu.Unlock()
	return nil
}

//listentable判断函数
func in(m map[string]*net.Listener, ip string) bool {
	for k, _ := range m {
		if k == ip {
			return true
		}
	}
	return false
}

//Api 处理函数
func f1(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		Listoendata, err := json.Marshal(Defaultapi)
		if err != nil {
			fmt.Println("解析ListenToend err: ", err)
			w.Write([]byte(fmt.Sprintln("解析ListenToend err: ", err)))
			return
		}
		w.Write(Listoendata)
	case "POST":
		postdata, err := ioutil.ReadAll(r.Body)
		if err != nil {
			fmt.Println("POST:读取r.Body失败err：", err)
			w.Write([]byte(fmt.Sprintln("POST:读取r.Body失败err：", err)))
			return
		}
		err = json.Unmarshal(postdata, Defaultapi)
		if err != nil {
			fmt.Println("POST调用反序列化失败err：", err)
			w.Write([]byte(fmt.Sprintln("POST调用反序列化失败err：", err)))
			return
		}
		go server(Defaultapi.Proxynowlisten, Defaultapi.Endnowlisten)
		w.Write(postdata)
	default:
		w.Write([]byte("<head>\n<title>HTTP ERR</title>\n</head>\n<body>\n<h1>ERR:你使用的请求方法有误，请使用正确方法！</h1>\n</body>"))
	}
}

func f2(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		getdata, err := json.Marshal(*Defaultapi.Nowallmap)
		if err != nil {
			fmt.Println("GET:json解析错误:", err)
			w.Write([]byte(fmt.Sprintln("GET:json解析错误:", err)))
			return
		}
		w.Write(getdata)
	case "POST":
		postdata, err := ioutil.ReadAll(r.Body)
		if err != nil {
			fmt.Println("POST:读取r.Body失败err：", err)
			w.Write([]byte(fmt.Sprintln("POST:读取r.Body失败err：", err)))
			return
		}
		err = json.Unmarshal(postdata, Defaultdel)
		if err != nil {
			fmt.Println("POST调用反序列化失败err：", err)
			w.Write([]byte(fmt.Sprintln("POST调用反序列化失败err：", err)))
			return
		}
		ok := in(Proxylistentable, Defaultdel.Listenaddr)
		if ok {
			err = unlistentable(Defaultdel.Listenaddr)
			if err != nil {
				fmt.Println("注销proxy server失败err：", err)
				w.Write([]byte(fmt.Sprintln("注销proxy server失败err：", err)))
				return
			}
			w.Write(postdata)
		} else {
			w.Write([]byte("请输入正确的地址！例：{\"deleteaddr\":\"<需要注销的addr>\"}"))
		}
	default:
		w.Write([]byte("<head>\n<title>HTTP ERR</title>\n</head>\n<body>\n<h1>ERR:你使用的请求方法有误，请使用正确方法！例：{\"deleteaddr\":\"<需要注销的addr>\"}</h1>\n</body>"))
	}
}

func f3(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		w.Write([]byte("当前api接口\n1./api(查看与调用新的proxy server.)\n2./cancel(查看与注销proxyserver本地监听列表。)\n  注销(推送消息体{\"deleteaddr\":\"<需要删除的IP>\"})"))
	default:
		w.Write([]byte("<head>\n<title>HTTP ERR</title>\n</head>\n<body>\n<h1>ERR:你使用的请求方法有误，请使用正确方法！</h1>\n</body>"))
	}
}

/*
***************************tcpserver部分***************************
 */
//Tcpserver 开启监听并转发监听(主要函数)
func server(sip string, dip string) {
	lis, err := net.Listen("tcp", sip)
	if err != nil {
		fmt.Println(err)
		return
	}
	Mu.Lock()
	ListenToend[sip] = dip
	Proxylistentable[sip] = &lis
	Mu.Unlock()
	defer lis.Close()
	defer delete(ListenToend, sip)
	var i int
	for {
		conn, err := lis.Accept()
		if err != nil {
			fmt.Printf("%v 建立连接错误:%v\n", time.Now().Format("2006-01-02 15:04:05"), err)
			time.Sleep(time.Second)
			i++
			if i == 5 {
				fmt.Println(time.Now().Format("2006-01-02 15:04:05"), sip, " :连接关闭!**************************************")
				return
			}
			continue
		}
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "有一个新的连接连到proxy_server：", conn.RemoteAddr(), "==>", conn.LocalAddr())
		go portproxy(conn, dip)
	}
}

//转发监听
func portproxy(sconn net.Conn, dip string) {
	defer sconn.Close()
	dconn, err := net.Dial("tcp", dip)
	if err != nil {
		fmt.Printf("连接%v失败:%v\n", dip, err)
		return
	}
	ExitChan := make(chan bool, 1)
	go func(sconn net.Conn, dconn net.Conn, Exit chan bool) {
		_, err := io.Copy(dconn, sconn)
		fmt.Printf("%v 往%v发送数据失败:%v\n", time.Now().Format("2006-01-02 15:04:05"), dip, err)
		ExitChan <- true
	}(sconn, dconn, ExitChan)
	go func(sconn net.Conn, dconn net.Conn, Exit chan bool) {
		_, err := io.Copy(sconn, dconn)
		fmt.Printf("%v 从%v接收数据失败:%v\n", time.Now().Format("2006-01-02 15:04:05"), dip, err)
		ExitChan <- true
	}(sconn, dconn, ExitChan)
	<-ExitChan
	dconn.Close()
}
